package gomoku_env

import (
	"fmt"
)

const (
	Empty                  = 0
	Player1     PlayerType = 1
	Player2     PlayerType = -1
	PlayerFocus            = 666
	BoardSize              = 3
	ChessNum               = 3
)

type PlayerType int

func (p *PlayerType) String() string {
	v := int(*p)
	return fmt.Sprint(v)
}

var PlayerMap = map[int]string{
	int(Player1): ".",
	int(Player2): "o",
	Empty:        " ",
	PlayerFocus:  "O",
}

type (
	State     [BoardSize][BoardSize]int
	GomokuEnv struct {
		board    State
		boardMap [BoardSize][BoardSize]string
		done     bool
		winner   PlayerType
		//
		Count  int64 // 记录落子数量
		Player PlayerType
	}
)

type Action struct {
	X int
	Y int
}

func NewGomokuEnv() *GomokuEnv {
	return &GomokuEnv{
		Player: Player1,
	}
}

func (env *GomokuEnv) Reset() [BoardSize][BoardSize]int {
	for i := range env.board {
		for j := range env.board[i] {
			env.board[i][j] = Empty
			env.boardMap[i][j] = PlayerMap[Empty]
		}
	}
	env.done = false
	env.winner = Empty
	env.Count = 0
	env.Player = Player1
	return env.board
}

func (env *GomokuEnv) Step(x, y int) ([BoardSize][BoardSize]int, bool, PlayerType, error) {
	player := PlayerType(1 - env.Count%2*2)
	env.Player = player
	return env.step(x, y, player)
}

func (env *GomokuEnv) step(x, y int, player PlayerType) ([BoardSize][BoardSize]int, bool, PlayerType, error) {
	if env.done {
		return env.board, env.done, env.winner, nil
	}
	invalid := false
	switch {
	case x >= BoardSize:
		invalid = true
	case y >= BoardSize:
		invalid = true
	case x < 0 || y < 0:
		invalid = true
	}

	if invalid || env.board[x][y] != Empty {
		return env.board, env.done, env.winner, fmt.Errorf("invalid action:%v,%v,player:%v", x, y, player)
	}
	env.board[x][y] = int(player)
	env.boardMap[x][y] = PlayerMap[int(player)]
	env.Count++
	env.done, env.winner = env.checkWinner(x, y, player)
	return env.board, env.done, env.winner, nil
}

func (env *GomokuEnv) checkWinner(x, y int, player PlayerType) (bool, PlayerType) {
	directions := [][2]int{{1, 0}, {0, 1}, {1, 1}, {1, -1}}

	for _, dir := range directions {
		count := 0
		for i := -(ChessNum - 1); i <= (ChessNum - 1); i++ {
			nx, ny := x+i*dir[0], y+i*dir[1]
			if nx >= 0 && nx < BoardSize && ny >= 0 && ny < BoardSize && env.board[nx][ny] == int(player) {
				count++
				if count == ChessNum {
					return true, player
				}
			} else {
				count = 0
			}
		}
	}

	isFull := env.Count == BoardSize*BoardSize
	if isFull {
		return true, Empty // Draw
	}
	return false, Empty
}

func (env *GomokuEnv) ShowBoardWithCursor(x, y int) {
	temp := env.boardMap[x][y]
	env.boardMap[x][y] = PlayerMap[PlayerFocus]
	env.ShowBoard()
	env.boardMap[x][y] = temp
}

func (env *GomokuEnv) ShowBoard() {
	// 打印列号
	fmt.Printf("%3d ", 1-env.Count%2*2)
	for i := 0; i < BoardSize; i++ {
		fmt.Printf("%3d ", i)
	}

	fmt.Println()
	fmt.Println()
	fmt.Println()

	for i := 0; i < BoardSize; i++ {
		// 打印行号
		fmt.Printf("%3d ", i)
		for j := 0; j < BoardSize; j++ {
			fmt.Printf("%3s ", env.boardMap[i][j])
		}
		fmt.Println()
		fmt.Println()
		fmt.Println()
	}
}

func Run() {
	env := NewGomokuEnv()
	env.Reset()
	env.Step(7, 7)  // Player1
	env.Step(8, 8)  // Player2
	env.Step(7, 8)  // Player1
	env.Step(8, 9)  // Player2
	env.Step(7, 9)  // Player1
	env.Step(8, 10) // Player2
	env.Step(7, 10) // Player1
	env.Step(8, 11) // Player2
	env.Step(7, 11) // Player1

	env.ShowBoard()
}

/*
*  打印示例
*     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
*  0  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  1  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  2  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  3  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  4  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  5  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  6  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
*  7  "  "  "  "  "  "  "  "  o  o  o  o  o  "  "
*  8  "  "  "  "  "  "  "  "  "  x  x  x  x  "  "
*  9  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
* 10  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
* 11  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
* 12  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
* 13  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
* 14  "  "  "  "  "  "  "  "  "  "  "  "  "  "  "
 */
